// Refer: https://dev.to/ik5/quick-and-dirty-audio-playing-in-golang-3n7c
// Only for Windows OS
package windows

import (
	"fmt"
	"path/filepath"
	"syscall"
	"unsafe"
)

const (
	SND_SYNC        uint = 0x0000     /* play synchronously (default) */
	SND_ASYNC       uint = 0x0001     /* play asynchronously */
	SND_NODEFAULT   uint = 0x0002     /* silence (!default) if sound not found */
	SND_MEMORY      uint = 0x0004     /* pszSound points to a memory file */
	SND_LOOP        uint = 0x0008     /* loop the sound until next sndPlaySound */
	SND_NOSTOP      uint = 0x0010     /* don't stop any currently playing sound */
	SND_NOWAIT      uint = 0x00002000 /* don't wait if the driver is busy */
	SND_ALIAS       uint = 0x00010000 /* name is a registry alias */
	SND_ALIAS_ID    uint = 0x00110000 /* alias is a predefined ID */
	SND_FILENAME    uint = 0x00020000 /* name is file name */
	SND_RESOURCE    uint = 0x00040004 /* name is resource name or atom */
	SND_PURGE       uint = 0x0040     /* purge non-static events for task */
	SND_APPLICATION uint = 0x0080     /* look for application specific association */
	SND_SENTRY      uint = 0x00080000 /* Generate a SoundSentry event with this sound */
	SND_RING        uint = 0x00100000 /* Treat this as a "ring" from a communications app - don't duck me */
	SND_SYSTEM      uint = 0x00200000 /* Treat this as a system sound */
	SND_ALIAS_START uint = 0          /* alias base */
)

// WinSound 提供Windows系统下的音频播放功能。
// 封装了Windows多媒体API(winmm.dll)的调用。
//
// 字段说明:
//   - inited: 是否已初始化
//   - mmsystem: winmm.dll模块句柄
//   - playSound: PlaySound函数指针
//   - sndPlaySoundA: sndPlaySoundA函数指针(ANSI版本)
//   - sndPlaySoundW: sndPlaySoundW函数指针(Unicode版本)
type WinSound struct {
	inited        bool
	mmsystem      *syscall.DLL
	playSound     *syscall.Proc
	sndPlaySoundA *syscall.Proc
	sndPlaySoundW *syscall.Proc
}

// init 初始化WinSound结构体，加载winmm.dll并获取函数指针。
// 该方法在首次调用其他方法时自动执行。
func (self *WinSound) init() {
	if !self.inited {
		self.mmsystem = syscall.MustLoadDLL("winmm.dll")
		self.playSound = self.mmsystem.MustFindProc("PlaySound")
		self.sndPlaySoundA = self.mmsystem.MustFindProc("sndPlaySoundA")
		self.sndPlaySoundW = self.mmsystem.MustFindProc("sndPlaySoundW")
		self.inited = true
	}
}

// PlaySound 播放系统声音或资源声音。
//
// 参数:
//   - sound: 系统声音别名或资源ID字符串
//   - hmod: 包含资源的模块句柄(0表示系统声音)
//   - flags: 播放标志(SND_*常量组合)
//
// 注意:
//   - 使用Unicode API版本
//   - 自动初始化
func (self *WinSound) PlaySound(sound string, hmod int, flags uint) {
	self.init()
	s16, _ := syscall.UTF16PtrFromString(sound)
	self.playSound.Call(uintptr(unsafe.Pointer(s16)), uintptr(hmod), uintptr(flags))
}

// SndPlaySoundA 播放音频文件(ANSI版本)。
//
// 参数:
//   - sound: 音频文件路径(ANSI编码)
//   - flags: 播放标志(SND_*常量组合)
//
// 注意:
//   - 使用ANSI API版本
//   - 自动初始化
//   - 路径长度限制为MAX_PATH(260字符)
func (self *WinSound) SndPlaySoundA(sound string, flags uint) {
	self.init()
	b := append([]byte(sound), 0)
	self.sndPlaySoundA.Call(uintptr(unsafe.Pointer(&b[0])), uintptr(flags))
}

// SndPlaySoundW 播放音频文件(Unicode版本)。
//
// 参数:
//   - sound: 音频文件路径(Unicode编码)
//   - flags: 播放标志(SND_*常量组合)
//
// 注意:
//   - 使用Unicode API版本
//   - 自动初始化
//   - 支持长路径(超过260字符)
//
// SndPlaySoundW 播放音频文件(Unicode版本)。
//
// 参数:
//   - sound: 音频文件路径(Unicode编码)
//   - flags: 播放标志(SND_*常量组合)
//
// 返回值:
//   - bool: 播放是否成功
//   - error: 错误信息(如果有)
//
// 注意:
//   - 使用Unicode API版本
//   - 自动初始化
//   - 支持长路径(超过260字符)
func (self *WinSound) SndPlaySoundW(sound string, flags uint) (bool, error) {
	self.init()
	s16, err := syscall.UTF16PtrFromString(sound)
	if err != nil {
		return false, fmt.Errorf("failed to convert string to UTF16: %v", err)
	}

	ret, _, _ := self.sndPlaySoundW.Call(uintptr(unsafe.Pointer(s16)), uintptr(flags))
	return ret != 0, nil
}

func TestSndPlaySoundW() {
	var ws WinSound
	fmt.Println("Getting files: ")
	files, err := filepath.Glob("*.wav")
	if err != nil {
		panic(err)
	}
	for _, file := range files {
		fmt.Printf("Playing %s ... ", file)
		ws.SndPlaySoundW(file, SND_SYNC)
		fmt.Println("done")
	}

	fmt.Println("Bye")
}
